fix(resolution): drop nested-local candidates from bare-name resolution (#1230)#1234
Open
umi008 wants to merge 1 commit into
Open
fix(resolution): drop nested-local candidates from bare-name resolution (#1230)#1234umi008 wants to merge 1 commit into
umi008 wants to merge 1 commit into
Conversation
…on (colbymchenry#1230) A Python attribute call like ', '.join(sorted(unresolved)) was matching a same-named local 'join' defined inside a different function of the same file (the user-facing example from the issue) and producing a wrong 'calls' edge. The same trap applied to any language where the same name lives lexically inside one function but is looked up from another — a class method named 'X' on Helper resolving as a callee of Worker's same-named method, or a JS/TS nested arrow function being matched as a callee of an outer function that simply shares the name. Root cause: bare-name resolution in matchByExactName and matchFuzzy ignored lexical scope. Same-file proximity promoted any same-named node in the file — including locals captured by a sibling function — and the qualifiedName-aware 'imported' checks did not run because the ref had no '.' or '::' to key them on. Fix: in matchByExactName and matchFuzzy, drop candidates whose enclosing scope is a function/method that the caller cannot reach lexically. 'Unreachable' means: same file, candidate's container is neither the caller's container nor a strict ancestor of the caller. Cross-file candidates pass through unchanged (imports, not lexical scope, govern their reachability). One-liner cases (C++ class 'class Calculator { Calculator(int){} };' where the class and its constructor share one line) are disambiguated by the extractor's qualifiedName hierarchy — a constructor's qn 'Calculator::Calculator' is recognized as a CHILD of the class 'Calculator', not the other way around. Validation: - new __tests__/python-builtin-scope-filter.test.ts: 3 tests, covering the literal-receiver repro from the issue, the positive case where the enclosing function calls its own nested helper, and the OOP variant (sibling class method). - full __tests__/resolution.test.ts: 169/169 pass, including the one-liner C++ instantiates regression test (colbymchenry#1035) that exercises class/method disambiguation by qualifiedName.
fc3b9b5 to
d3ee4ca
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Bare-name resolution in
matchByExactNameandmatchFuzzywas promoting any same-named node in the caller's file as a candidate, including locals captured by a sibling function. A Python attribute call like", ".join(sorted(unresolved))was matching a same-named localjoindefined inside a different function of the same file and producing a wrongcallsedge.Root cause
Same-file proximity is the right tiebreaker in general, but it has no business crossing function boundaries. The qualifiedName-aware and import-aware strategies don't help here because the ref has no
.or::to key them on — it is a bare identifier likejoin— so the resolver fell all the way through to the proximity-only fallback.Fix
In
matchByExactNameandmatchFuzzy, drop candidates whose enclosing scope is a function/method the caller cannot reach lexically.One-liner cases (C++
class Calculator { Calculator(int){} };where the class and its constructor share one line) are disambiguated by the extractor's qualifiedName hierarchy: a constructor'sCalculator::Calculatoris recognized as a CHILD of the classCalculator, not the other way around, so the constructor isn't mistakenly treated as the class's container.Validation
__tests__/python-builtin-scope-filter.test.ts: 3/3 pass, covering the literal-receiver repro from the issue, the positive case where the enclosing function calls its own nested helper, and the OOP variant (sibling class method named the same).__tests__/resolution.test.ts: 169/169 pass — including the one-liner C++ instantiates regression test (C++ stack-allocation syntax not recorded as 'instantiates' edge #1035) that exercises class/method disambiguation by qualifiedName. Theinstantiatesedge forCalculator calc(0)correctly targetsclass:Calculator, not the same-named constructor method.npm run build: clean (no TS warnings, no errors).Closes #1230.